var interestedBadges = ['Deputy', 'Civic Duty', 'Convention', 'Strunk & White', 'Electorate', 'Sportsmanship', 'Pundit', 'Copy Editor', 'Outspoken'],
    electionLength = 23 * 24 * 60 * 60 * 1000;
    
Meddle.Configure({
    key: 'LYwuXpduJM2Kk9Twsc008w(('
});

if(!Date.now) {
    Date.now = function () {
        return +new Date();
    };
}

var Settings = {};

// MarkdownMini parser, regexs are from the chat source *cough cough* 
var Markdown = {
    parse: function (a) {
        a = a.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
        
        a = a.replace(/\\`/g, "&#96;")
             .replace(/\\\*/g, "&#42;")
             .replace(/\\_/g, "&#95;")
             .replace(/\\\[/g, "&#91;")
             .replace(/\\\]/g, "&#93;")
             .replace(/\\\(/g, "&#40;")
             .replace(/\\\)/g, "&#41;");
        
        a = a.replace(this._code, "$1<code>$2</code>")
             .replace(this._bold, "$1<strong>$2</strong>")
             .replace(this._italic, "$1<em>$2</em>")
             .replace(this._link, '$1<a href="$3">$2</a>')
             .replace(this._autolink, ' <a href="$2">$2</a>');
        
        return a;
    },
    _bold: /(^|[\s,(])(?:\*\*|__)(?=\S)(.+?\S)(?:\*\*|__)(?=[\s,?!.;:)]|$)/g,
    _italic: /(^|[\s,(])(?:\*|_)(?=\S)(.+?\S)(?:\*|_)(?=[\s,?!.;:)]|$)/g,
    _link: /(^|\s)\[([^\]]+)\]\(((?:https?|ftp):\/\/[^)\s]+?)(?:\s(?:"|&quot;)([^"]+?)(?:"|&quot;))?\)/g,
    _autolink: /([^">;]|^)\b((?:https?|ftp):\/\/[A-Za-z0-9][-A-Za-z0-9+&@#/%?=~_|\[\]\(\)!:,.;]*[-A-Za-z0-9+&@#/%=~_|\[\]])/gi,
    _code: /(^|\W)`(.+?)`(?=\W|$)/g
};

var Loading = {
    _listEle: $('#loading-list li'),
    complete: function(title) {
        this._listEle.filter('#load-' + title).addClass('loaded');
        
        if(!this._listEle.filter(':visible:not(.loaded)').length) {
            $('#loading-list, #loading').slideUp(400);
        }
    },
    add: function(name, text, insertAfter) {
        var insertSelector = insertAfter ? '#load-' + insertAfter : ':last';
        
        $('<li>', {
            text: text,
            id: 'load-' + name,
            'class': 'cleanup'
        }).insertAfter(insertSelector);
    }, 
    reset: function(users) {
        $('#loading, #loading-list').slideDown();
        this._listEle.removeClass('loaded').filter('cleanup').remove();
        
        if(users) this._listEle.filer('.load-election').hide();
    }
};

// Copied from the chat source :P
function repNumber(a) {
    if (a < 10000) {
        return a;
    } else if (a < 100000) {
        var d = Math.floor(Math.round(a / 100) / 10);
        a = Math.round((a - d * 1000) / 100);
        
        return d + (a > 0 ? "." + a : "") + ("k");
    } else {
        return Math.round(a / 1000) + "k";
    }
}

// Normalize time
function normalizeTime (time) {
    var timeago = new Date(Date.now() - (time * 1000)),
        dateString = '';
    
    var dateComponent = {
        year: timeago.getFullYear() - 1970,
        month: timeago.getMonth()
    }
    
    for(var i in dateComponent) {
        if(dateComponent[i])
            dateString += dateComponent[i] + (' ' + i) + (dateComponent[i] > 1 ? 's' : '') + ' ';
    }
    
    return $.trim(dateString);
}

function findBadge (name) {
    for(var i = 0, l = badgeInfo.length; i < l; i++){
        if(badgeInfo[i].name === name){
            return badgeInfo[i];
        }
    }
    
    return false;
}

$(document).ready(function () {
    $.get('sites.json', function (sites) {
        // The page is ready, remove the loading placeholder
        $('#loading-sites').slideUp(300);
        $('#sitelist').slideDown(300);

        var siteHash = window.location.hash.replace('#', '');

        for (var site in sites) {
            var current = sites[site],
                started;

            if (siteHash && site === 'http://' + siteHash + '.com') {
                $('#sitelist').hide();
                
                Settings = {
                    siteName: site.match(/^http:\/\/(.*?)\.com\/?/)[1],
                    friendlyName: current.name,
                    siteLogo: current.icon
                };
    
                init();
            }

            if (current.elections.length) {
                started = current.elections[0];
                current.url = site;
                
                if (Date.now() - started < electionLength) {
                    insertInto('#current-elections', current, 'icon');
                } else {
                    insertInto('#completed-elections', current, 'favicon');
                }
            }
        }
    });
    
    function insertInto(target, site, image) {
        var children, inserted = false,
            siteName = site.url.match(/^http:\/\/(.*?)\.com\/?/)[1],
            item = $('<li>', {
                    'data-sitename': siteName
                }),
            started = site.elections[0];

        var anchor = $('<a>', {
            href: '#' + siteName, 
            text: site.name
        }).appendTo(item);

        $("<img>", {
            src: site[image], 
            alt: site.name + ' logo'
        }).prependTo(anchor);

        
        target = $(target);
        children = target.children('li');
        
        item.data('started', started);
        
        children.each(function (i, current) {
            current = $(current);
        
            if (current.data('started') < started) {
                current.before(item);
                
                return !(inserted = true);
            }
        });
        
        if (!inserted) {
            target.append(item);
        }
    }
});

$('#sitelist').delegate('li', 'click', function () {
    var t = $(this), siteFriendlyName = $.trim(t.text());
    
    $('#sitelist').slideUp(600);
    
    Settings = {
        siteName: $.trim(this.getAttribute('data-sitename').replace(/\s/g, '').toLowerCase()),
        friendlyName: siteFriendlyName,
        siteLogo: t.find('img').attr('src').replace('favicon.ico', 'apple-touch-icon.png')
    };
    
    init();
});

function init() {
    $('#sort').slideDown()
        .find('h2 + label').addClass('active')
        .siblings().removeClass('active');
    
    $('#user-list').empty();
    
    Loading.reset();
    
    $('#election-result').slideUp();
    
    // Pull in data into site info section
    var siteInfo = $('#site-detail, #option-menu').slideUp();
    
    siteInfo.find('dl').empty();
    
    $('.cleanup').remove();
    
    var source = 'http://' + Settings.siteName + '.com/election?tab=nomination';
    getUserInfo(source);
}

function buildUserProfile (users) {
    var localIds = [],
        networkIds = [],
        count = users.length;
    
    // Fisher-Yates shuffle should be better than a sort(Math.random())
    for(var i = 0, j, tmp; i < count; ++i) {
        j = Math.floor(Math.random() * (i + 1));
        tmp = users[i];
        users[i] = users[j];
        users[j] = tmp;
        
        localIds.push(tmp.user_id);
        networkIds.push(tmp.account_id);
    }
    
    var userTemplate = $('#userInitTemplate'),
        metaTemplate = $('#metaTemplate'),
        networkTemplate = $('#stackExchangeTemplate'),
        tagTemplate = $('#userTagTemplate'),
        timelineTemplate = $('#timelineTemplate'),
        rendered = {};
        
    users.forEach(function (user) {
        var website;
    
        if (user.website_url) {
            website = '<a href="' + user.website_url + '">' + user.website_url + '</a>';
        }
        
        var personal = $.grep([user.age, user.location, website], function (c) { return c != null; }),
            current = $.extend({
                rep_string:      repNumber(user.reputation),
                days_active:     (Date.now() / 1000 - user.creation_date) / (24 * 60 * 60),
                member_for:      normalizeTime(user.creation_date),
                creation_string: new Date(user.creation_date * 1000).toString(),
                user_text:       user.text,
                nominated_order: user.nominated_order,
                personal_date:   personal
            }, user);

        rendered["local-" + current.user_id] = rendered["network-" + current.account_id] = userTemplate.tmpl(current).appendTo('#user-list');
    });
    
    function pullMeta(userIds) {
        Meddle.With('meta.' + Settings.siteName).users(userIds).filter('!*Mq.)f.7fClai1MW').get(function (users) {
            users.forEach(function (user) {
                user.rep_string = repNumber(user.reputation);
                metaTemplate.tmpl(user).appendTo(rendered["network-" + user.account_id].find('.meta'));
            });
            
            Loading.complete('meta');
        });
    }
    
    Meddle.associated(networkIds).pagesize(100).all().get(function (users) {
        var tallies = {}, metaIds = [];
        
        users.forEach(function (user) {
            var tally = tallies[user.account_id];
            
            if (!tally) {
                tally = {
                    total: 0,
                    active: 0,
                    reputation: 0
                };
                tallies[user.account_id] = tally;
            }
            
            if (user.site_name === 'Meta ' + Settings.friendlyName) {
                metaIds.push(user.user_id);
            }
            
            ++tally.total;
            tally.reputation += user.reputation - 100;
            
            if (user.reputation > 200) {
                ++tally.active;
            }
        });
        
        Object.keys(tallies).forEach(function (networkId) {
            var tally = tallies[networkId];
        
            networkTemplate.tmpl({
                combined_rep: tally.reputation,
                combined_rep_string: repNumber(tally.reputation),
                active_sites: tally.active,
                total_sites: tally.total
            }).appendTo(rendered["network-" + networkId].find('.stackexchange'));
        });
        
        if (Settings.siteName === 'stackoverflow') {
            pullMeta(metaIds);
        }
    });
    
    // For every other site, do Meta poll here
    if (Settings.siteName !== 'stackoverflow') {
        pullMeta(localIds);
    }
    
    var siteRoute = Meddle.With(Settings.siteName),
        usersRoute = siteRoute.users(localIds),
        userIdList = localIds.join(';');
        
    // This is not ideal, since we're basically requesting a bunch of
    // information and hoping it just happens to cover out needs. 
    //
    // TODO: See if we can request that the API allow us to ask for
    //       data groups so we can only make a request for the bare
    //       minimum amount of data instead. Probably won't go over
    //       well because of the additional backend complexity for
    //       top n per id, but worth a shot to avoid this situation
    usersRoute.tags().pagesize(100).pages(Math.ceil(Math.min(400, count * 30) / 100)).get(function (tags) {
        tags.forEach(function (tag) {
            var tagList = rendered["local-" + tag.user_id].find('.tag_list');
            
            tag.name_safe = encodeURIComponent(tag.name);
            tag = tagTemplate.tmpl(tag).appendTo(tagList);
            
            if (tagList.children().length > 9) {
                tag.hide();
            }
        });
        
        Loading.complete('tag');
        
        usersRoute.badges().pagesize(100).all().sort('rank').asc().get(function (badges) {
            var ranks = ['bronze', 'silver', 'gold'],
                unpopulated = $('.badge_list ul:empty');
                
            interestedBadges.forEach(function (badge) {
                var info = findBadge(badge);
                
                $('<li>', {
                    text: badge,
                    title: info.rank + ': ' + info.description
                }).appendTo(unpopulated);
            });
            
            badges.forEach(function (badge) {
                if (badge.badge_type === 'tag_based') {
                    var tag = rendered['local-' + badge.user.user_id].find('.tag_list a').filter(match);
                    
                    if (tag.length && !tag[0].className) {
                        tag.addClass(badge.rank);
                    }
                } else if (interestedBadges.indexOf(badge.name) !== -1) {
                    rendered['local-' + badge.user.user_id].find('.badge_list li').filter(match).addClass('badge_received');
                }
                
                function match() {
                    return (this.textContent || this.innerText) === badge.name;
                }
            });
            
            $('.tag_list').each(function () {
                $(this).children().show().sortElements(function (a, b) {
                    var rank = ranks.indexOf(b.className) - ranks.indexOf(a.className);
                    
                    return rank ? rank : $(b).data('tmplItem').data.count - $(a).data('tmplItem').data.count;
                }).slice(9).hide();
            });
            
            Loading.complete('badge');
        });
    });
    
    // Ask for the last five bits of activity for each user in separate requests
    var responses = 0, timelineConversions = {
        commented: 'comment',
        revision:  'edit',
        answered:  'answer',
        asked:     'question',
        suggested: 'suggestion', // This one will be too long, but most candidates have edit privs...right?
        reviewed:  'review'
    };
    
    for (var i = 0; i < count; ++i) {
        siteRoute.users(localIds[i]).timeline().pagesize(5).pages(1).filter('!nNik2mH5PA').get(function (activity) {
            if (activity.length) {
                var activityList = rendered['local-' + activity[0].user_id].find('.recent_activity ul');
                
                activity.forEach(function (activity) {
                    var action = timelineConversions[activity.timeline_type] || activity.timeline_type;
                
                    activity = $.extend({
                        title:           '',
                        detail:          '',
                        action:          action,
                        time_ago:        $.timeago(new Date(activity.creation_date * 1000)),
                        creation_string: new Date(activity.creation_date * 1000).toString(),
                    }, activity);
                    
                    timelineTemplate.tmpl(activity).appendTo(activityList);
                });
            }
            
            if (++responses === count) {
                Loading.complete('activity');
            }
        });
    }
}

function getUserInfo(source) {
    var requestPath = source.replace('http://', '').replace('.com/', '/'),
        electionPage = source.replace(/\?.*$/, '');

    // Start ajax
    $.get(requestPath, function(data) {
        var page = $(data);
        
        if(page.find('.elections').length){
            var elections = page.find('.elections tr:last td:first-child a');
            var url = 'http://' + Settings.siteName + '.com' + elections.attr('href') + '?tab=nomination';
            
            getUserInfo(url);
        } else {
            Loading.complete('init');
            
            // Pull Elections page data
            var siteDetail = $('#site-detail'),
                electionTitle = page.find('h1:first').text(),
                dl = siteDetail.find('dl'),
                electionStatus = $.trim(page.find('.question-status').text()),
                finished = (electionStatus.indexOf('election ended') !== -1);
                
            siteDetail.find('img.site-logo').attr('src', Settings.siteLogo).end()
                .find('h2.site-title').text(Settings.friendlyName + ' ' + electionTitle).end()
                .find('a.election-link').attr('href', electionPage).end();
            
            page.find('.module').find('p.label-key').each(function(){
                var t = $(this);
                
                $('<dt>', {
                    text: t.text()
                }).appendTo(dl);
                
                $('<dd>', {
                    text: t.next().text(),
                    title: t.next().attr('title')
                }).appendTo(dl);
            });
            
            // Has we have elected yo?
            if(finished){
                var electedCandidates = page.find('.question-status a:has(img)')
                    .appendTo('#election-result').addClass('cleanup');
                
                var results = electedCandidates.attr('href', function(i, v){
                        return 'http://' + Settings.siteName + '.com' + v;
                    }).children('img').attr('src', function(i, v){
                        return 'http://' + Settings.siteName + '.com' + v;
                    }).map(function(){
                        return this.getAttribute('src').match(/\/(\d+)\./)[1];
                    }).get();
                
                $('#election-result').slideDown();
            }
            
            siteDetail.slideDown();
            
            // Grab list of withdrawn candidates
            var wrapper = document.createElement('div'), withdrawn = [];
            wrapper.innerHTML = data;
            var withdrawnPosts = wrapper.querySelectorAll('#mainbar tr script + div[id^="post-"] .user-gravatar32 a');
            
            for (var i = withdrawnPosts.length - 1; i >= 0; i--) {
                withdrawn.push(+withdrawnPosts[i].getAttribute('href').split('/')[2]);
            }
            
            // Parse and process data from the elections page
            Settings.phase = (finished ? 'completed' : electionStatus.split(' ')[0].toLowerCase());
            
            var users = {};
            
            page.find('[id^=post-]').each(function (idx, element) {
                var post = $(element),
                    // TODO: This (and a few other data sourced from page scraping) are far too fragile. 
                    // We can't keep changing the code whenever the SE election page changes. This is suboptimal. 
                    userId = +post.find('.user-details a').attr('href').split('/')[4];
                    
                if (withdrawn.indexOf(user) === -1) {
                    var user = {
                        user_id: userId,
                        post_id: element.id.substring('post-'.length),
                        text: post.find('.post-text').html(),
                        nominated_order: idx
                    };
                    
                    if (finished) {
                        user.election = results.indexOf(userId) !== -1;
                    }
                    
                    users[userId] = user;
                }
            });
            
            Meddle.With(Settings.siteName).users(Object.keys(users)).filter('!*Mq.)f.7fClai1MW').get(function (candidates) {
                Loading.complete('profile');
                
                for (var i = 0; i < candidates.length; ++i) {
                    $.extend(candidates[i], users[candidates[i].user_id] || {});
                    
                    if (/^http:\/\/www.gravatar.com\//.test(candidates[i].profile_image)) {
                        candidates[i].profile_image += '&s=90';
                    }
                }
                
                buildUserProfile(candidates);
            });
            
            Meddle.With(Settings.siteName).users().pagesize(30).pages(1).get(function (users) {
                users.forEach(function (user) {
                    $.extend(user, {
                        rep_string: repNumber(user.reputation)
                    });
                    
                    $('#userInfoBox').tmpl(user).appendTo('#add-user-list').addClass('cleanup top-user');
                });
            });
        }
    });
}

// Event handlers for the user lists
var timeout;

function getUsers(text) {
    Meddle.With(Settings.siteName).users().pagesize(30).pages(1).set('inname', text).get(function (users) {
        $('#add-user-list li')
            .filter('.top-user').hide().end()
            .filter('.search-user').remove();
            
        users.forEach(function (user) {
            $.extend(user, {
                rep_string: repNumber(user.reputation)
            });
            
            $('#userInfoBox').tmpl(user).appendTo('#add-user-list').addClass('cleanup top-user');
        });
    });
}

$('#selected-user-list').delegate('li', 'click', function(){
    $(this).appendTo('#add-user-list');
});

$('#add-user-list').delegate('li', 'click', function(){
    $(this).appendTo('#selected-user-list');
});

$('#user-search').bind('input', function(){
    if(!this.value) {
        $('#add-user-list li').hide().filter('.top-user').show();
    } else {
        clearTimeout(timeout);
        timeout = setTimeout(getUsers, 500, this.value);
    }
});

// Add user
$('#add-user-confirm').click(function(){
    var userList = $('#selected-user-list li').map(function(){
        return $(this).data('tmplItem').data;
    }).get();
    
    $('#selected-user-list').empty();
    
    if(userList.length) {
        buildUserProfile(userList);
    }
    
    $('#add-user').slideUp();
    
    return false;
});

// Toggle
$('a[id$=toggle]').click(function(){
    $('#' + this.id.replace('-toggle', '')).slideToggle();
    return false;
});


// Populate badge option box with list of interesting badges
for(var i = 0; i < interestedBadges.length; i++){
    $('#defaultBadgeTemplate').tmpl(findBadge(interestedBadges[i])).appendTo('#default-tag-list');
}

$('#default-tag-list').delegate('a.badge-list-remove', 'click', function(event){
    var $t = $(this).parent(); 
    
    interestedBadges.splice($.inArray($t.data('tmplItem').data.name, interestedBadges), 1);
    
    $t.slideUp(300, function(){
        $(this).remove();
    });
    
    event.preventDefault();
});

var suggestions = $('#badge_suggestions_list');

$('#add-badge').bind('input', function(){
    suggestions.empty();
    
    if(this.value){
        var badges = [];
        
        for(var i = 0, l = badgeInfo.length; i < l; i++){
            if(badgeInfo[i].name.toLowerCase().indexOf(this.value.toLowerCase()) !== -1 && 
                $.inArray(badgeInfo[i].name, interestedBadges) === -1){
                badges.push(badgeInfo[i]);
            }
        }
        
        $.each(badges, function(i, v){
            $('<span>', {
                text: v.name, 
                'class': [v.rank, 'badge-name'].join(' '), 
                title: v.description,
                click: function(){
                    $('#defaultBadgeTemplate').tmpl(v).appendTo('#default-tag-list');
                    interestedBadges.push(v.name);
                    
                    $(this).slideUp(300, function(){
                        $(this).remove();
                    });
                    
                    return false;
                }
            }).appendTo(suggestions);
        });
    }
});

// Sorting
$('#sort input:radio').click(function(){
    $(this).parent().addClass('active').siblings().removeClass('active');
    
    if(this.name === 'sort') {
        var sortFunc;
        
        switch (this.value) {
            case 'random':
                sortFunc = function () {
                    return Math.random() * 3 - 1;
                }
                break;
            case 'reputation':
                sortFunc = function (a, b) {
                    return $(b).find('.rep').attr('title') - $(a).find('.rep').attr('title');
                }
                break;
            case 'join':
                sortFunc = function (a, b) {
                    return (new Date($(a).find('.member_for').attr('title').substring(14)) -
                            new Date($(b).find('.member_for').attr('title').substring(14)));
                }
                break;
            case 'nomination':
                var sortFunc = function (a, b) {
                    return a.getAttribute('data-nomination-order') - b.getAttribute('data-nomination-order');
                }
                break;
        }
        
        $('#user-list > li').sortElements(sortFunc);
    } else if (this.name === 'show') {
        $('.nomination_detail').hide().filter('.' + this.value).show();
    }
});

$('#sort').hover(function(){
    $(this).children().fadeTo(100, 0.9);
}, function(){
    $(this).children().fadeTo(100, 0.3);
});



// Unicorns!!!
$('#unicorn').toggle(function(){
    $('img').attr('src', function(i, v){
        return v.replace(/\w*\.gravatar\.com/i, 'unicornify.appspot.com');
    });
}, function(){
    $('img').attr('src', function(i, v){
        return v.replace('unicornify.appspot.com', 'www.gravatar.com');
    });
})
